"""
运行管理器 - 负责文件运行功能的封装
"""

import os
import sys
import subprocess
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtCore import QObject, pyqtSignal


class RunManager(QObject):
    """运行管理器类"""
    
    # 定义信号：运行状态变化
    run_started = pyqtSignal(str)  # 文件路径
    run_finished = pyqtSignal(str, int)  # 文件路径，退出码
    output_received = pyqtSignal(str)  # 输出内容
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.main_window = parent
    
    def run_current_file(self):
        """运行当前文件"""
        # 检查是否有主窗口和工作区
        if not self.main_window or not hasattr(self.main_window, 'workspace'):
            QMessageBox.warning(None, "警告", "无法访问工作区")
            return False
        
        # 获取当前文件路径
        file_path = self.main_window.workspace.get_current_file_path()
        
        # 检查文件路径
        if not file_path:
            QMessageBox.information(None, "提示", "没有当前文件，请先保存文件")
            return False
        
        if not os.path.exists(file_path):
            QMessageBox.information(None, "提示", "文件不存在，请先保存文件")
            return False
        
        # 获取文件扩展名
        file_ext = os.path.splitext(file_path)[1].lower()
        
        if not file_ext:
            QMessageBox.information(None, "提示", "文件没有扩展名，无法确定运行方式")
            return False
        
        # 根据文件类型选择运行方式
        try:
            if file_ext == '.py':
                return self.run_python_file(file_path)
            elif file_ext == '.java':
                return self.run_java_file(file_path)
            elif file_ext in ['.html', '.htm']:
                return self.run_html_file(file_path)
            elif file_ext in ['.js', '.mjs', '.cjs']:
                return self.run_javascript_file(file_path)
            elif file_ext == '.ts':
                return self.run_typescript_file(file_path)
            else:
                QMessageBox.information(None, "提示", f"当前版本暂不支持运行 {file_ext} 文件")
                return False
        except Exception as e:
            QMessageBox.critical(None, "运行错误", f"运行文件时发生错误: {str(e)}")
            return False
    
    def run_python_file(self, file_path):
        """运行Python文件"""
        # 检查Python是否可用
        if not self.is_python_available():
            QMessageBox.warning(None, "警告", "未找到Python环境，请确保Python已安装并添加到PATH")
            return False
        
        # 检查文件是否可读
        if not os.access(file_path, os.R_OK):
            QMessageBox.critical(None, "错误", "Python文件不可读")
            return False
        
        # 发送运行开始信号
        self.run_started.emit(file_path)
        
        # 显示终端（如果可用）
        if hasattr(self.main_window, 'terminal'):
            self.main_window.terminal.setVisible(True)
            # 确保终端有合理的显示比例
            if hasattr(self.main_window, 'vertical_splitter'):
                current_sizes = self.main_window.vertical_splitter.sizes()
                if len(current_sizes) > 1 and current_sizes[1] < 100:  # 如果终端太小
                    self.main_window.vertical_splitter.setSizes([600, 200])
        
        # 在终端显示运行信息
        if hasattr(self.main_window, 'terminal'):
            self.main_window.terminal.append_output(f"> 运行Python文件: {file_path}")
            # self.main_window.terminal.append_output(f"> 使用Python: {self.get_python_executable()}")
            # self.main_window.terminal.append_output("> " + "=" * 40)
        
        try:
            # 获取工作目录
            working_dir = os.path.dirname(file_path)
            if not os.path.exists(working_dir):
                working_dir = os.getcwd()
            
            # 构建命令
            python_cmd = self.get_python_executable()
            cmd = [python_cmd, file_path]
            
            # 运行Python文件
            # 统一使用UTF-8编码处理输入输出
            env = os.environ.copy()
            env['PYTHONIOENCODING'] = 'utf-8'
            
            result = subprocess.run(
                cmd, 
                capture_output=True, 
                text=True, 
                encoding='utf-8',  # 统一使用UTF-8编码
                errors='replace',  # 遇到无法解码的字符时替换为占位符
                cwd=working_dir, 
                timeout=300,  # 5分钟超时
                env=env
            )
            
            # 显示输出结果
            if hasattr(self.main_window, 'terminal'):
                if result.stdout:
                    self.main_window.terminal.append_output("输出:")
                    self.main_window.terminal.append_output(result.stdout)
                
                if result.stderr:
                    self.main_window.terminal.append_output("错误:")
                    self.main_window.terminal.append_output(result.stderr)
                
                self.main_window.terminal.append_output(f"程序退出码: {result.returncode}")
                #self.main_window.terminal.append_output("> " + "=" * 40)
            
            # 发送运行完成信号
            self.run_finished.emit(file_path, result.returncode)
            
            # 在状态栏显示结果
            if hasattr(self.main_window, 'status_bar'):
                if result.returncode == 0:
                    self.main_window.status_bar.showMessage("Python程序运行成功")
                else:
                    self.main_window.status_bar.showMessage("Python程序运行失败")
            
            return True
            
        except subprocess.TimeoutExpired:
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output("错误: Python程序运行超时（5分钟）")
            
            QMessageBox.critical(None, "错误", "Python程序运行超时（5分钟）")
            self.run_finished.emit(file_path, -1)
            return False
            
        except Exception as e:
            error_msg = f"运行Python文件时发生错误: {str(e)}"
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output(f"错误: {error_msg}")
            
            QMessageBox.critical(None, "错误", error_msg)
            self.run_finished.emit(file_path, -1)
            return False
    
    def run_java_file(self, file_path):
        """运行Java文件"""
        # 检查Java是否可用
        if not self.is_java_available():
            QMessageBox.warning(None, "警告", "未找到Java环境，请确保JDK已安装并添加到PATH")
            return False
        
        # 检查文件是否可读
        if not os.access(file_path, os.R_OK):
            QMessageBox.critical(None, "错误", "Java文件不可读")
            return False
        
        # 发送运行开始信号
        self.run_started.emit(file_path)
        
        # 显示终端（如果可用）
        if hasattr(self.main_window, 'terminal'):
            self.main_window.terminal.setVisible(True)
            # 确保终端有合理的显示比例
            if hasattr(self.main_window, 'vertical_splitter'):
                current_sizes = self.main_window.vertical_splitter.sizes()
                if len(current_sizes) > 1 and current_sizes[1] < 100:  # 如果终端太小
                    self.main_window.vertical_splitter.setSizes([600, 200])
        
        # 在终端显示运行信息
        if hasattr(self.main_window, 'terminal'):
            self.main_window.terminal.append_output(f"> 运行Java文件: {file_path}")
        
        try:
            # 获取工作目录和类名
            working_dir = os.path.dirname(file_path)
            if not os.path.exists(working_dir):
                working_dir = os.getcwd()
            
            file_name = os.path.basename(file_path)
            class_name = os.path.splitext(file_name)[0]
            
            # 检测系统编码
            import locale
            system_encoding = locale.getpreferredencoding()
            
            # 环境变量设置
            env = os.environ.copy()
            
            # 第一步：编译Java文件
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output("> 正在编译...")
            
            compile_cmd = ['javac', '-encoding', 'UTF-8', file_path]
            compile_result = subprocess.run(
                compile_cmd,
                capture_output=True,
                text=True,
                encoding='utf-8',
                errors='replace',
                cwd=working_dir,
                timeout=60,  # 1分钟编译超时
                env=env
            )
            
            # 检查编译是否成功
            if compile_result.returncode != 0:
                if hasattr(self.main_window, 'terminal'):
                    self.main_window.terminal.append_output("编译错误:")
                    if compile_result.stderr:
                        self.main_window.terminal.append_output(compile_result.stderr)
                    if compile_result.stdout:
                        self.main_window.terminal.append_output(compile_result.stdout)
                
                QMessageBox.critical(None, "编译错误", "Java文件编译失败，请查看终端输出")
                self.run_finished.emit(file_path, compile_result.returncode)
                return False
            
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output("> 编译成功，正在运行...")
            
            # 第二步：运行编译后的类文件
            run_cmd = ['java', class_name]
            
            # 使用系统默认编码来捕获输出，避免中文乱码
            run_result = subprocess.run(
                run_cmd,
                capture_output=True,
                text=True,
                encoding=system_encoding,  # 使用系统编码
                errors='replace',
                cwd=working_dir,
                timeout=300,  # 5分钟运行超时
                env=env
            )
            
            # 显示输出结果
            if hasattr(self.main_window, 'terminal'):
                if run_result.stdout:
                    self.main_window.terminal.append_output("输出:")
                    self.main_window.terminal.append_output(run_result.stdout)
                
                if run_result.stderr:
                    self.main_window.terminal.append_output("错误:")
                    self.main_window.terminal.append_output(run_result.stderr)
                
                self.main_window.terminal.append_output(f"程序退出码: {run_result.returncode}")
            
            # 发送运行完成信号
            self.run_finished.emit(file_path, run_result.returncode)
            
            # 在状态栏显示结果
            if hasattr(self.main_window, 'status_bar'):
                if run_result.returncode == 0:
                    self.main_window.status_bar.showMessage("Java程序运行成功")
                else:
                    self.main_window.status_bar.showMessage("Java程序运行失败")
            
            return True
            
        except subprocess.TimeoutExpired:
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output("错误: Java程序运行超时")
            
            QMessageBox.critical(None, "错误", "Java程序运行超时")
            self.run_finished.emit(file_path, -1)
            return False
            
        except Exception as e:
            error_msg = f"运行Java文件时发生错误: {str(e)}"
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output(f"错误: {error_msg}")
            
            QMessageBox.critical(None, "错误", error_msg)
            self.run_finished.emit(file_path, -1)
            return False
    
    def run_html_file(self, file_path):
        """运行HTML文件 - 在浏览器中打开"""
        # 检查文件是否可读
        if not os.access(file_path, os.R_OK):
            QMessageBox.critical(None, "错误", "HTML文件不可读")
            return False
        
        # 发送运行开始信号
        self.run_started.emit(file_path)
        
        # 在终端显示信息
        if hasattr(self.main_window, 'terminal'):
            self.main_window.terminal.setVisible(True)
            if hasattr(self.main_window, 'vertical_splitter'):
                current_sizes = self.main_window.vertical_splitter.sizes()
                if len(current_sizes) > 1 and current_sizes[1] < 100:
                    self.main_window.vertical_splitter.setSizes([600, 200])
            self.main_window.terminal.append_output(f"> 在浏览器中打开HTML文件: {file_path}")
        
        try:
            # 将文件路径转换为file:// URL
            import urllib.parse
            from urllib.request import pathname2url
            
            # 转换为绝对路径
            abs_path = os.path.abspath(file_path)
            # 转换为URL格式
            file_url = 'file:///' + pathname2url(abs_path).replace('///', '/')
            
            # 在默认浏览器中打开
            import webbrowser
            success = webbrowser.open(file_url)
            
            if success:
                if hasattr(self.main_window, 'terminal'):
                    self.main_window.terminal.append_output(f"已在浏览器中打开: {file_url}")
                
                if hasattr(self.main_window, 'status_bar'):
                    self.main_window.status_bar.showMessage("已在浏览器中打开HTML文件")
                
                self.run_finished.emit(file_path, 0)
                return True
            else:
                if hasattr(self.main_window, 'terminal'):
                    self.main_window.terminal.append_output("错误: 无法打开浏览器")
                
                QMessageBox.warning(None, "警告", "无法打开默认浏览器")
                self.run_finished.emit(file_path, -1)
                return False
                
        except Exception as e:
            error_msg = f"打开HTML文件时发生错误: {str(e)}"
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output(f"错误: {error_msg}")
            
            QMessageBox.critical(None, "错误", error_msg)
            self.run_finished.emit(file_path, -1)
            return False
    
    def run_javascript_file(self, file_path):
        """运行JavaScript文件（使用Node.js）"""
        # 检查Node.js是否可用
        if not self.is_nodejs_available():
            QMessageBox.warning(None, "警告", "未找到Node.js环境，请确保Node.js已安装并添加到PATH")
            return False
        
        # 检查文件是否可读
        if not os.access(file_path, os.R_OK):
            QMessageBox.critical(None, "错误", "JavaScript文件不可读")
            return False
        
        # 发送运行开始信号
        self.run_started.emit(file_path)
        
        # 显示终端
        if hasattr(self.main_window, 'terminal'):
            self.main_window.terminal.setVisible(True)
            if hasattr(self.main_window, 'vertical_splitter'):
                current_sizes = self.main_window.vertical_splitter.sizes()
                if len(current_sizes) > 1 and current_sizes[1] < 100:
                    self.main_window.vertical_splitter.setSizes([600, 200])
            self.main_window.terminal.append_output(f"> 运行JavaScript文件: {file_path}")
        
        try:
            # 获取工作目录
            working_dir = os.path.dirname(file_path)
            if not os.path.exists(working_dir):
                working_dir = os.getcwd()
            
            # 构建命令
            node_cmd = self.get_nodejs_executable()
            cmd = [node_cmd, file_path]
            
            # 运行JavaScript文件
            # Node.js默认使用UTF-8输出，统一使用UTF-8编码
            env = os.environ.copy()
            
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                encoding='utf-8',  # Node.js使用UTF-8输出
                errors='replace',
                cwd=working_dir,
                timeout=300,  # 5分钟超时
                env=env
            )
            
            # 显示输出结果
            if hasattr(self.main_window, 'terminal'):
                if result.stdout:
                    self.main_window.terminal.append_output("输出:")
                    self.main_window.terminal.append_output(result.stdout)
                
                if result.stderr:
                    self.main_window.terminal.append_output("错误:")
                    self.main_window.terminal.append_output(result.stderr)
                
                self.main_window.terminal.append_output(f"程序退出码: {result.returncode}")
            
            # 发送运行完成信号
            self.run_finished.emit(file_path, result.returncode)
            
            # 在状态栏显示结果
            if hasattr(self.main_window, 'status_bar'):
                if result.returncode == 0:
                    self.main_window.status_bar.showMessage("JavaScript程序运行成功")
                else:
                    self.main_window.status_bar.showMessage("JavaScript程序运行失败")
            
            return True
            
        except subprocess.TimeoutExpired:
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output("错误: JavaScript程序运行超时")
            
            QMessageBox.critical(None, "错误", "JavaScript程序运行超时")
            self.run_finished.emit(file_path, -1)
            return False
            
        except Exception as e:
            error_msg = f"运行JavaScript文件时发生错误: {str(e)}"
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output(f"错误: {error_msg}")
            
            QMessageBox.critical(None, "错误", error_msg)
            self.run_finished.emit(file_path, -1)
            return False
    
    def run_typescript_file(self, file_path):
        """运行TypeScript文件（使用ts-node或tsc+node）"""
        # 检查Node.js是否可用
        if not self.is_nodejs_available():
            QMessageBox.warning(None, "警告", "未找到Node.js环境，请确保Node.js已安装并添加到PATH")
            return False
        
        # 检查文件是否可读
        if not os.access(file_path, os.R_OK):
            QMessageBox.critical(None, "错误", "TypeScript文件不可读")
            return False
        
        # 发送运行开始信号
        self.run_started.emit(file_path)
        
        # 显示终端
        if hasattr(self.main_window, 'terminal'):
            self.main_window.terminal.setVisible(True)
            if hasattr(self.main_window, 'vertical_splitter'):
                current_sizes = self.main_window.vertical_splitter.sizes()
                if len(current_sizes) > 1 and current_sizes[1] < 100:
                    self.main_window.vertical_splitter.setSizes([600, 200])
            self.main_window.terminal.append_output(f"> 运行TypeScript文件: {file_path}")
        
        try:
            # 获取工作目录
            working_dir = os.path.dirname(file_path)
            if not os.path.exists(working_dir):
                working_dir = os.getcwd()
            
            # Node.js默认使用UTF-8输出
            env = os.environ.copy()
            
            # 尝试使用ts-node（更快）
            if self.is_tsnode_available():
                if hasattr(self.main_window, 'terminal'):
                    self.main_window.terminal.append_output("> 使用ts-node运行...")
                
                cmd = ['ts-node', file_path]
                result = subprocess.run(
                    cmd,
                    capture_output=True,
                    text=True,
                    encoding='utf-8',  # Node.js使用UTF-8输出
                    errors='replace',
                    cwd=working_dir,
                    timeout=300,
                    env=env
                )
            else:
                # 如果没有ts-node，尝试使用tsc编译后运行
                if not self.is_tsc_available():
                    QMessageBox.warning(None, "警告", "未找到TypeScript编译器（tsc）或ts-node。\n\n请安装：npm install -g typescript 或 npm install -g ts-node")
                    self.run_finished.emit(file_path, -1)
                    return False
                
                if hasattr(self.main_window, 'terminal'):
                    self.main_window.terminal.append_output("> 正在编译TypeScript...")
                
                # 编译TypeScript
                file_name = os.path.basename(file_path)
                js_file = os.path.splitext(file_path)[0] + '.js'
                
                compile_cmd = ['tsc', file_path]
                compile_result = subprocess.run(
                    compile_cmd,
                    capture_output=True,
                    text=True,
                    encoding='utf-8',
                    errors='replace',
                    cwd=working_dir,
                    timeout=60,
                    env=env
                )
                
                if compile_result.returncode != 0:
                    if hasattr(self.main_window, 'terminal'):
                        self.main_window.terminal.append_output("编译错误:")
                        if compile_result.stderr:
                            self.main_window.terminal.append_output(compile_result.stderr)
                        if compile_result.stdout:
                            self.main_window.terminal.append_output(compile_result.stdout)
                    
                    QMessageBox.critical(None, "编译错误", "TypeScript文件编译失败，请查看终端输出")
                    self.run_finished.emit(file_path, compile_result.returncode)
                    return False
                
                if hasattr(self.main_window, 'terminal'):
                    self.main_window.terminal.append_output("> 编译成功，正在运行...")
                
                # 运行编译后的JS文件
                node_cmd = self.get_nodejs_executable()
                run_cmd = [node_cmd, js_file]
                result = subprocess.run(
                    run_cmd,
                    capture_output=True,
                    text=True,
                    encoding='utf-8',  # Node.js使用UTF-8输出
                    errors='replace',
                    cwd=working_dir,
                    timeout=300,
                    env=env
                )
            
            # 显示输出结果
            if hasattr(self.main_window, 'terminal'):
                if result.stdout:
                    self.main_window.terminal.append_output("输出:")
                    self.main_window.terminal.append_output(result.stdout)
                
                if result.stderr:
                    self.main_window.terminal.append_output("错误:")
                    self.main_window.terminal.append_output(result.stderr)
                
                self.main_window.terminal.append_output(f"程序退出码: {result.returncode}")
            
            # 发送运行完成信号
            self.run_finished.emit(file_path, result.returncode)
            
            # 在状态栏显示结果
            if hasattr(self.main_window, 'status_bar'):
                if result.returncode == 0:
                    self.main_window.status_bar.showMessage("TypeScript程序运行成功")
                else:
                    self.main_window.status_bar.showMessage("TypeScript程序运行失败")
            
            return True
            
        except subprocess.TimeoutExpired:
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output("错误: TypeScript程序运行超时")
            
            QMessageBox.critical(None, "错误", "TypeScript程序运行超时")
            self.run_finished.emit(file_path, -1)
            return False
            
        except Exception as e:
            error_msg = f"运行TypeScript文件时发生错误: {str(e)}"
            if hasattr(self.main_window, 'terminal'):
                self.main_window.terminal.append_output(f"错误: {error_msg}")
            
            QMessageBox.critical(None, "错误", error_msg)
            self.run_finished.emit(file_path, -1)
            return False
    
    def is_python_available(self):
        """检查Python是否可用"""
        try:
            subprocess.run([self.get_python_executable(), "--version"], 
                         capture_output=True, timeout=5)
            return True
        except:
            return False
    
    def get_python_executable(self):
        """获取Python可执行文件路径"""
        # 优先使用系统PATH中的python
        if sys.platform == "win32":
            # 在Windows上，尝试不同版本的Python
            for cmd in ["python", "python3", "py"]:
                try:
                    result = subprocess.run(["where", cmd], capture_output=True, text=True)
                    if result.returncode == 0:
                        return cmd
                except:
                    continue
            return "python"
        else:
            # 在Linux/macOS上
            for cmd in ["python3", "python"]:
                try:
                    result = subprocess.run(["which", cmd], capture_output=True, text=True)
                    if result.returncode == 0:
                        return cmd
                except:
                    continue
            return "python3"
    
    def is_java_available(self):
        """检查Java是否可用"""
        try:
            # 检查javac（编译器）
            subprocess.run(["javac", "-version"], 
                         capture_output=True, timeout=5)
            # 检查java（运行时）
            subprocess.run(["java", "-version"], 
                         capture_output=True, timeout=5)
            return True
        except:
            return False
    
    def is_nodejs_available(self):
        """检查Node.js是否可用"""
        try:
            subprocess.run([self.get_nodejs_executable(), "--version"],
                         capture_output=True, timeout=5)
            return True
        except:
            return False
    
    def is_tsnode_available(self):
        """检查ts-node是否可用"""
        try:
            subprocess.run(["ts-node", "--version"],
                         capture_output=True, timeout=5)
            return True
        except:
            return False
    
    def is_tsc_available(self):
        """检查TypeScript编译器是否可用"""
        try:
            subprocess.run(["tsc", "--version"],
                         capture_output=True, timeout=5)
            return True
        except:
            return False
    
    def get_nodejs_executable(self):
        """获取Node.js可执行文件路径"""
        if sys.platform == "win32":
            # 在Windows上
            for cmd in ["node", "node.exe"]:
                try:
                    result = subprocess.run(["where", cmd], capture_output=True, text=True)
                    if result.returncode == 0:
                        return cmd
                except:
                    continue
            return "node"
        else:
            # 在Linux/macOS上
            for cmd in ["node", "nodejs"]:
                try:
                    result = subprocess.run(["which", cmd], capture_output=True, text=True)
                    if result.returncode == 0:
                        return cmd
                except:
                    continue
            return "node"
    
    def detect_file_type(self, file_path):
        """检测文件类型"""
        if not os.path.exists(file_path):
            return "unknown"
        
        file_ext = os.path.splitext(file_path)[1].lower()
        
        # 编程语言文件
        if file_ext in ['.py']:
            return 'python'
        elif file_ext in ['.java']:
            return 'java'
        elif file_ext in ['.js', '.ts']:
            return 'javascript'
        elif file_ext in ['.html', '.htm']:
            return 'html'
        elif file_ext in ['.cpp', '.c', '.h', '.hpp']:
            return 'cpp'
        elif file_ext in ['.go']:
            return 'go'
        elif file_ext in ['.rs']:
            return 'rust'
        elif file_ext in ['.php']:
            return 'php'
        elif file_ext in ['.rb']:
            return 'ruby'
        elif file_ext in ['.swift']:
            return 'swift'
        elif file_ext in ['.kt', '.kts']:
            return 'kotlin'
        
        # 脚本文件
        elif file_ext in ['.sh', '.bash']:
            return 'shell'
        elif file_ext in ['.bat', '.cmd']:
            return 'batch'
        elif file_ext in ['.ps1']:
            return 'powershell'
        
        # 配置和数据文件
        elif file_ext in ['.json', '.xml', '.yaml', '.yml', '.ini', '.cfg', '.conf']:
            return 'config'
        elif file_ext in ['.txt', '.md', '.log']:
            return 'text'
        
        else:
            return 'unknown'
    
    def get_supported_file_types(self):
        """获取支持的文件类型"""
        return {
            'python': ['.py'],
            'java': ['.java'],
            'javascript': ['.js', '.ts'],
            'html': ['.html', '.htm'],
            'cpp': ['.cpp', '.c', '.h', '.hpp'],
            'shell': ['.sh', '.bash'],
            'batch': ['.bat', '.cmd'],
            'powershell': ['.ps1'],
            'config': ['.json', '.xml', '.yaml', '.yml', '.ini', '.cfg', '.conf'],
            'text': ['.txt', '.md', '.log']
        }